/*
 * Created on Feb 14, 2005
 * Created by dfhuynh
 */
package edu.mit.simile.longwell.ui;

import it.okkam.longwell.ui.command.BrowseDefaultModelCommand;
import it.okkam.longwell.ui.command.HomePageCommand;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.runtime.RuntimeConstants;
import org.mortbay.http.ResourceCache;
import org.mortbay.util.Resource;

import edu.mit.simile.longwell.LongwellServer;
import edu.mit.simile.longwell.query.Query;
import edu.mit.simile.longwell.ui.command.BrowseCommand;
import edu.mit.simile.longwell.ui.command.ExportCommand;
import edu.mit.simile.longwell.ui.command.FocusCommand;
import edu.mit.simile.longwell.ui.command.IFlairCommand;
import edu.mit.simile.longwell.ui.command.StartCommand;
import edu.mit.simile.longwell.ui.command.StopCommand;
import edu.mit.simile.longwell.ui.command.ViewCommand;

/**
 * @author dfhuynh
 */
public class FlairServlet extends HttpServlet {
    final static private Logger s_logger = Logger.getLogger(FlairServlet.class);

    protected VelocityEngine 	m_ve;
    protected Map 				m_getCommands = new HashMap();
    protected Map 				m_postCommands = new HashMap();
    protected ResourceCache		m_resourceCache;

    /* (non-Javadoc)
     * @see javax.servlet.GenericServlet#init(javax.servlet.ServletConfig)
     */
    public void init(ServletConfig config) throws ServletException {
        super.init(config);

        ServletContext context = config.getServletContext();
        File flairDir = new File(context.getInitParameter("flairDir"));

        try {
            File clientSideDir = new File(flairDir, "client-side");

            m_resourceCache = new ResourceCache();
            m_resourceCache.setResourceBase(clientSideDir.getCanonicalPath());
            m_resourceCache.start();
        } catch (Exception e) {
            s_logger.error("Failed to start resource cache", e);
            throw new ServletException("Failed to start resource cache", e);
        }

        try {
            m_ve = new VelocityEngine();
            m_ve.init(createInitProperties(flairDir));

            createCommands(config);
        } catch (Exception e) {
            s_logger.error("Failed to initialize Velocity engine", e);
            throw new ServletException("Failed to initialize Velocity engine", e);
        }
    }

    /* (non-Javadoc)
     * @see javax.servlet.GenericServlet#destroy()
     */
    public void destroy() {
        try {
            m_resourceCache.stop();
            m_resourceCache = null;
        } catch (InterruptedException e) {
            s_logger.error("Failed to stop resource cache", e);
        }

        super.destroy();
    }

    /* (non-Javadoc)
     * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        String pathInContext = request.getPathInfo();
        if (pathInContext.startsWith("/resources/")) {
            serveResource(request, response);
        } else {
            FlairMessage msg = makeMessage(request, response);
            String command = msg.m_query.getFirstParamValue("command");
            if (command == null) {
                command = "start";
            }

            IFlairCommand flairCommand = (IFlairCommand) m_getCommands.get(command);
            if (command != null) {
                response.setContentType("text/html");
                flairCommand.execute(msg);
            } else {
                response.getOutputStream().println("No such command: " + command);
            }
        }
    }

    /* (non-Javadoc)
     * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        FlairMessage msg = makeMessage(request, response);
        String command = msg.m_query.getFirstParamValue("command");
        if (command == null) {
            command = "start";
        }

        IFlairCommand flairCommand = (IFlairCommand) m_postCommands.get(command);
        if (flairCommand != null) {
            response.setContentType("text/html");
            flairCommand.execute(msg);
        } else {
            response.getOutputStream().println("No such command: " + command);
        }
    }

    protected Properties createInitProperties(File flairDir) throws Exception {
        Properties properties = new Properties();

        File flairServerSideDir = new File(flairDir, "server-side");

        // Templates
        properties.setProperty(
            RuntimeConstants.FILE_RESOURCE_LOADER_PATH,
            flairServerSideDir.getAbsolutePath());

        // Macros
        File[] files = new File(flairServerSideDir, "macros").listFiles();
        String s = "";

        for (int i = 0; i < files.length; i++) {
            File file = files[i];
            if (!file.isHidden()) {
                String name = file.getName();
                if (name.endsWith(".vm")) {
                    s = s + (s.length() == 0 ? "" : ",") + "macros/" + name;
                }
            }
        }
        properties.setProperty("velocimacro.library", s);

        // Other properties
        FileInputStream fis = new FileInputStream(
            new File(flairServerSideDir, "velocity.properties"));

        properties.load(fis);
        fis.close();

        return properties;
    }

    protected void createCommands(ServletConfig config) {
        InjectionManager injections = createInjectionManager();

        m_getCommands.put("start", new StartCommand(injections, "templates/commands/start.vt"));
        m_getCommands.put("home", new HomePageCommand(injections, "templates/commands/home.vt"));
        m_getCommands.put("browse", new BrowseCommand(injections));
        m_getCommands.put("browse-default-model", new BrowseDefaultModelCommand(injections));
        m_getCommands.put("focus", new FocusCommand(injections));
        m_getCommands.put("export", new ExportCommand(injections));

        m_postCommands.put("view", new ViewCommand(injections, "templates/views/object-property-list.vt"));
        m_postCommands.put("view-referers", new ViewCommand(injections, "templates/components/referers.vt"));
        m_postCommands.put("export", new ExportCommand(injections));
        // TODO(SM): this was done so that we can stop gracefull in those cases
        //           where the process get shut down without the JVM being signalled
        //           (for example, when running inside eclipse). But it's
        //           commented out because it's a potential security concern.
        //           In the future, we should make this configurable.
        //m_getCommands.put("stop", new StopCommand());
    }

    protected InjectionManager createInjectionManager() {
        InjectionManager injections = new InjectionManager();

        injections.put("common-stylesheets", "templates/components/common-stylesheets.vt");
        injections.put("common-scripts", "templates/components/common-scripts.vt");
        injections.put("browse-stylesheets", "templates/components/browse-stylesheets.vt");
        injections.put("browse-scripts", "templates/components/browse-scripts.vt");
        injections.put("start-stylesheets", "templates/components/start-stylesheets.vt");
        injections.put("start-scripts", "templates/components/start-scripts.vt");
        injections.put("focus-stylesheets", "templates/components/focus-stylesheets.vt");
        injections.put("focus-scripts", "templates/components/focus-scripts.vt");

        injections.put("page-title", "templates/components/page-title.vt");
        injections.put("header-slide", "templates/components/header-slide.vt");
        injections.put("item-count", "templates/components/item-count.vt");

        injections.put("item-view", "templates/views/object-property-list.vt");
        injections.put("value-view", "templates/views/value.vt");
        injections.put("map-results-pane", "templates/panes/map-results-pane.vt");
        injections.put("export-gmap-results", "templates/export/gmap-results.vt");
        return injections;
    }

    protected FlairMessage makeMessage(
            HttpServletRequest request,
            HttpServletResponse response
        ) {
        String profileID = LongwellServer.getServer().getProfile(request.getSession().getId()).getID();

        String extraPath = request.getPathInfo();
        if (extraPath.length() > 0) {
            String s = URLDecoder.decode(
                extraPath.substring(extraPath.startsWith("/") ? 1 : 0));

            if (s.length() > 0) {
                profileID = s;
            }
        }

        Query query = new Query(request.getQueryString());

        return new FlairMessage(null, request, response, m_ve, profileID, query, "");
    }

    protected void serveResource(
        HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {

        String pathInContext =
            request.getPathInfo().substring("/resources".length());

        Resource resource = m_resourceCache.getResource(pathInContext);
        if (resource != null && resource.exists()) {
            ResourceCache.ResourceMetaData metaData =
                (ResourceCache.ResourceMetaData)resource.getAssociate();

            response.setContentType(metaData.getMimeType());
            resource.writeTo(response.getOutputStream(), 0, resource.length());
        }
    }
}
